﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using TekaMUD.Engine.Framework;

namespace TekaMUD.Engine.Commands.Parser
{
	/// <summary>
	/// Parses player input strings into a Command node.
	/// </summary>
	public class CommandLineParser
	{
		/// <summary>
		/// Returns a Command node based on the player input.
		/// </summary>
		/// <returns>In case of an empty input string, returns an EmptyCommandNode</returns>
		public static CommandNode Parse(string input)
		{
			// Empty command check.
			if (input == null || input.Trim().Length == 0)
				return new EmptyCommandNode();

			StringTokenizer st = new StringTokenizer(input);
			LinkedList<string> ret = st.Parse();


			#region Second step, build Nodes

			CommandNode rootNode;

			// First one is command node.
			rootNode = new CommandNode(ret.ElementAt(0));

			if (ret.Count == 1)
				goto finish;


			// Search for prepositions.
			int prepositionIndex = -1;

			for (int i = 1; i < ret.Count; i++)
			{
				if (MatchPreposition(ret.ElementAt(i)))
				{
					prepositionIndex = i;
					break;
				}
			}

			if (prepositionIndex > -1)
			{
				// append all values between Verb and Preposition
				rootNode.ArgumentList = ProcessArguments(ret, 1, prepositionIndex - 1);
				rootNode.Preposition = new PrepositionNode(ret.ElementAt(prepositionIndex));


				if (prepositionIndex < ret.Count - 2)
					throw new Exception("More than one argument at target!");
				else
					rootNode.Target = new ArgumentNode(ret.ElementAt(ret.Count - 1));

			}
			else
			{
				// append all values between Verb and EOL
				rootNode.ArgumentList = ProcessArguments(ret, 1, ret.Count - 1);
				rootNode.Preposition = null;
			}


			#endregion

		finish:

			StringBuilder sb = new StringBuilder();

			foreach (string part in ret)
				sb.Append(part + " ");

			rootNode.FullText = sb.ToString().Trim();

			Logger.LogDebug("Built command " + rootNode.Name + " with " + rootNode.ArgumentList.Count + " arguments.");

			return rootNode;
		}

		/// <summary>
		/// Processa uma lista de strings e vírgulas. Adiciona as strings individualmente, até uma vírgula
		/// </summary>
		private static ArgumentList ProcessArguments(LinkedList<string> list, int startOffset, int endOffset)
		{
			ArgumentList ret = new ArgumentList();
			StringBuilder builder = new StringBuilder();
			bool outside = true;

			for (int i = startOffset; i <= endOffset; i++)
			{
				string token = list.ElementAt(i);

				// Caso parvo: ", xxxx"
				if (outside && token == ",")
					continue;
				else if (!outside && token == ",")
				{
					// push do builder como um "node". Reset do builder e estado.
					ret.Children.AddLast(new ArgumentNode(builder.ToString().Trim()));
					builder = new StringBuilder();
					outside = true;
				}
				else
				{
					outside = false;
					builder.Append(token);
					builder.Append(" ");
				}
			}

			if (!outside)
				ret.Children.AddLast(new ArgumentNode(builder.ToString().Trim()));



			return ret;
		}

		private static bool MatchPreposition(string target)
		{
			foreach (PrepositionType pt in Enum.GetValues(typeof(PrepositionType)))
			{
				if (pt.ToString().ToLower() == target.ToLower())
					return true;
			}

			return false;
		}
	}


	public enum PrepositionType
	{
		To,
		For,
		With
	}

	public class Node
	{
		public string Name { get; set; }
		public Node(string name) { Name = name; }
		public Node() { }
	}

	public class CommandNode : Node
	{
		public CommandNode()
		{
			ArgumentList = new ArgumentList();
		}

		public CommandNode(string name)
			: base(name)
		{
			ArgumentList = new ArgumentList();
		}

		public ArgumentList ArgumentList { get; set; }
		public ArgumentNode Target { get; set; }
		public PrepositionNode Preposition { get; set; }
		public String FullText { get; set; }

		public override string ToString()
		{
			return Name;
		}
	}

	public class EmptyCommandNode : CommandNode { }

	public class PrepositionNode : Node
	{
		public PrepositionType Type { private set; get; }

		public PrepositionNode(PrepositionType type)
		{
			Type = type;
		}

		public PrepositionNode(string preposition)
		{
			Type = (PrepositionType)Enum.Parse(typeof(PrepositionType), preposition, true);
		}

		public override string ToString()
		{
			return Type.ToString();
		}

		public static explicit operator string(PrepositionNode an)
		{
			return an.Type.ToString();
		}
	}

	public class ArgumentNode : Node
	{
		public string[] NounComponents { get; private set; }

		public ArgumentNode(string name)
		{
			Name = name;
			NounComponents = name.Split(' ');
		}

		public override string ToString()
		{
			return Name;
		}

		public static explicit operator string(ArgumentNode an)
		{
			return an.Name;
		}
	}

	public class ArgumentList : Node, IEnumerable
	{
		public LinkedList<ArgumentNode> Children { private set; get; }

		public ArgumentList()
		{
			Children = new LinkedList<ArgumentNode>();
		}

		public IEnumerator GetEnumerator()
		{
			return Children.GetEnumerator();
		}

		public int Count
		{
			get { return Children.Count; }
		}

		public ArgumentNode this[int index]
		{
			get
			{
				return Children.ElementAt(index);
			}
		}

	}
}